// 委托的语法更像 JavaScript 中的函数表达式
public class 委托
{
    // 定义委托
    // 语法可能看起来像是声明变量，但它实际上是声明类型。 
    // 可以在类中、直接在命名空间中、甚至是在全局命名空间中定义委托类型。
    public delegate int 委托类型(ref int number);
    // public 委托类型? 委托实例;

    static int 注册函数(ref int number)
    {
        // number *= 2;
        return number * 2;
        // return 0;
    }

    static int 注册函数2(ref int number)
    {
        return number * 4;
    }

    public static void 练习()
    {
        int param = 2;

        // 实例化委托
        // 将处理函数作为参数传入委托的构造函数内
        // 处理函数的参数列表和返回值须与声明的委托类型的参数列表和返回值相同
        // 四种实例化方式
        委托类型 委托实例 = 注册函数;
        // var 委托实例2 = new 委托类型(注册函数);
        // 委托类型 委托实例3 = new(注册函数);

        // 委托实例作为处理函数的容器（JavaScript 函数表达式）执行处理函数的逻辑
        // Console.WriteLine(委托实例(ref param));

        // （多播委托）可向委托实例注入多个函数
        委托实例 += 注册函数2;

        // 移除某个函数
        // 委托实例 -= 注册函数2;

        // 多播委托的返回值
        Console.WriteLine($"多播委托的返回值为最后向委托实例注入的函数的返回值：{委托实例?.Invoke(ref param)}");
        System.Console.WriteLine("一个具有非空返回值的多播委托通常是没有意义的，因为仅最后一个函数的返回结果有效\n通常，多播委托的返回类型为 void");

        // int num = 2;
        // num = num * 2;
        // System.Console.WriteLine(num);

        // 调用多播委托的所有注入函数并获取返回值
        int counter = 0;
        foreach (委托类型 item in 委托实例.GetInvocationList())
        {
            int[] array = { 42, 13 };
            System.Console.WriteLine($"调用多播委托的所有注入函数并获取返回值 {item(ref array[counter])}");
            counter++;
        }

        // .NET预定义的委托
        // 都可以使用泛型
        Action 预定义委托;//无参数无返回值
        Action<string,int> 预定义委托2;//有两个参数无返回值
        Func<object> 预定义委托3;//有一个有返回值
        Func<int,string,char> 预定义委托4;//有两个参数返回值类型为 char
    }
}